package com.dhcc.bpm.business.papc.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.dhcc.bpm.business.papc.entity.BatchRecord;
import com.dhcc.bpm.business.papc.entity.BatchSchedule;
import com.dhcc.bpm.business.papc.entity.JobInfo;
import com.dhcc.bpm.business.papc.entity.ZjywTxCfg;
import com.dhcc.bpm.business.papc.service.IBatchRecordService;
import com.dhcc.bpm.business.papc.service.IBatchScheduleService;
import com.dhcc.bpm.business.papc.service.IJobInfoService;
import com.dhcc.bpm.business.papc.service.IZjywTxCfgService;
import com.dhcc.bpm.business.papc.vo.JobInfoVO;
import com.dhcc.bpm.common.annotation.Log;
import com.dhcc.bpm.common.enums.BusinessType;
import com.dhcc.bpm.common.utils.StringUtils;
import com.dhcc.bpm.common.utils.http.HttpClientResult;
import com.dhcc.bpm.common.utils.http.HttpClientUtil;
import com.dhcc.bpm.modules.system.service.ISerialNumberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import org.quartz.CronExpression;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.quartz.spi.OperableTrigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.log.annotation.ApiLog;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 控制器
 *
 * @author sjm
 * @since 2020-12-11
 */
@RestController
@AllArgsConstructor
@NoArgsConstructor
@RequestMapping("bpm-papc/jobinfo")
@Api(value = "定时任务控制器", tags = "接口")
public class JobInfoController extends BladeController {

    private static final String ANY = "*";

    @Value("${batchSchedule.refreshAll.url}")
    private String refreshUrl;
//	private static final String refreshUrl = "http://127.0.0.1:8802/msfsw-pub-batch-task-service/refresh/all";

    private static final Logger LOGGER = LoggerFactory.getLogger(JobInfoController.class);
    @Autowired
    private IJobInfoService jobInfoService;
    @Autowired
    private IZjywTxCfgService zjywTxCfgService;
    @Autowired
    private IBatchRecordService batchRecordService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private IBatchScheduleService batchScheduleService;


    /**
     * 将字符串日期转成cron表达式
     *
     * @param dateJsonObject 将 分时日周月分解成 重复值和定时值
     * @return trigger&repeat
     */
    private String decomposition(JSONObject dateJsonObject) {
        StringBuilder cronExpression = new StringBuilder();
        if (dateJsonObject.containsKey("trigger") && StringUtils.isNotBlank(dateJsonObject.getString("trigger"))) {
            // trigger为""或为null,都会默认为任意，
            cronExpression.append(" ").append(dateJsonObject.getString("trigger"));
        } else {
            cronExpression.append(" *");
        }
        if (dateJsonObject.containsKey("repeat") && dateJsonObject.getIntValue("repeat") > 0) {
            // 大于0为有效输入
            cronExpression.append("/").append(dateJsonObject.getIntValue("repeat"));
        }
        return cronExpression.toString();
    }

    /**
     * 将cron的JSON字符串转换成cron表达式
     *
     * @param cornJsonObject cron的JSON对象
     * @return cron表达式
     */
    private String conversion(JSONObject cornJsonObject) {
        StringBuilder cronExpression = new StringBuilder();

        JSONObject second = cornJsonObject.getJSONObject("second");
        if (second.containsKey("trigger") && StringUtils.isNotBlank(second.getString("trigger"))) {
            cronExpression.append(second.getString("trigger"));
        } else {
            cronExpression.append("*");
        }
        if (second.containsKey("repeat") && second.getIntValue("repeat") > 0) {
            // 大于0为有效数字
            cronExpression.append("/").append(second.getString("repeat"));
        }

        JSONObject minute = cornJsonObject.getJSONObject("minute");
        cronExpression.append(decomposition(minute));

        JSONObject hour = cornJsonObject.getJSONObject("hour");
        cronExpression.append(decomposition(hour));

        JSONObject day = cornJsonObject.getJSONObject("day");
        cronExpression.append(decomposition(day));

        JSONObject month = cornJsonObject.getJSONObject("month");
        cronExpression.append(decomposition(month));

        JSONObject week = cornJsonObject.getJSONObject("week");
        if (week.containsKey("trigger")) {
            JSONArray weekArray = week.getJSONArray("trigger");
            if (!weekArray.isEmpty()) {
                cronExpression.append(" ");
                List<String> list = JSONObject.parseArray(weekArray.toJSONString(), String.class);
                for (String str : list) {
                    cronExpression.append(weekToEnglish(str)).append(",");
                }
                cronExpression.deleteCharAt(cronExpression.length() - 1);
            } else {
                cronExpression.append(" ?");
            }
        } else {
            cronExpression.append(" ?");
        }
        if (week.containsKey("repeat")) {
            if (StringUtils.isNotBlank(week.getString("repeat"))) {
                cronExpression.append("/").append(week.getString("repeat"));
            }
        }
        return cronExpression.toString();
    }


    /**
     * 将前端页面传入的字符串，转换成定时任务表达式
     *
     * @param cronStr 前端传入cron的JSON字符串
     * @return cron表达式
     */
    @GetMapping("/getCronStr")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "根据前端传的时间字符串转化成cron表达式", notes = "字符串化的json")
    public R<String> getCronStr(String cronStr) {
        JSONObject cornJsonObject = JSONObject.parseObject(cronStr);
        return R.data(conversion(cornJsonObject));
    }

    public static List<String> cronExpressionToExecuteDates(String cronExpression, int size) throws ParseException {
        List<String> resultList = new ArrayList<>();
        CronTriggerImpl cronTriggerImpl = new CronTriggerImpl();
        //这里写要准备猜测的cron表达式
        cronTriggerImpl.setCronExpression(cronExpression);
        Calendar calendar = Calendar.getInstance();
        Date now = calendar.getTime();

        //把统计的区间段设置为从现在到2年后的今天（主要是为了方法通用考虑，如那些1个月跑一次的任务，如果时间段设置的较短就不足20条)
        calendar.add(Calendar.YEAR, 2);

        //这个是重点，一行代码搞定~~
        List<Date> dates = computeFireTimesBetween(cronTriggerImpl, null, now, calendar.getTime(), size);

        for (Date date : dates) {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            resultList.add(dateFormat.format(date));
        }
        return resultList;
    }

    public static List<Date> computeFireTimesBetween(OperableTrigger trigg, org.quartz.Calendar cal, Date from, Date to, int size) {
        LinkedList<Date> lst = new LinkedList<>();
        OperableTrigger t = (OperableTrigger) trigg.clone();
        if (t.getNextFireTime() == null) {
            t.setStartTime(from);
            t.setEndTime(to);
            t.computeFirstFireTime(cal);
        }

        while (lst.size() < size + 1) {
            Date d = t.getNextFireTime();
            if (d == null) {
                break;
            }

            if (d.before(from)) {
                t.triggered(cal);
            } else {
                if (d.after(to)) {
                    break;
                }

                lst.add(d);
                t.triggered(cal);
            }
        }

        return Collections.unmodifiableList(lst);
    }


    /**
     * 检验定时任务表达式是否有效
     * 有效时返回 最近10次运行时间
     * 无效时返回表达式错误描述
     *
     * @param cronExpression cron表达式
     * @return true/false
     */
    @GetMapping("/validate")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "根据前端传的时间字符串转化成cron表达式", notes = "字符串化的json")
    public R<JSONObject> validate(String cronExpression) {
        String msg;
        JSONObject result = new JSONObject();
        if (CronExpression.isValidExpression(cronExpression)) {
            msg = "有效";
            result.put("isValidExpression", true);
            try {
                result.put("eg", cronExpressionToExecuteDates(cronExpression, 10));
            } catch (ParseException pe) {
                msg = pe.getMessage();
            }
        } else {
            msg = "无效";
            result.put("isValidExpression", false);
            try {
                new CronExpression(cronExpression);
            } catch (ParseException pe) {
                msg = pe.getMessage();
            }
        }
        return R.data(result, msg);
    }


    private static JSONObject strToJsonOnject(String str) {
        JSONObject jsonObject = new JSONObject();
        if (str.contains("/")) {
            String[] array = str.split("/");
            if (!ANY.equals(array[0])) {
                jsonObject.put("trigger", array[0]);
            }
            jsonObject.put("repeat", array[1]);
        } else {
            if (!ANY.equals(str)) {
                jsonObject.put("trigger", str);
            }
        }
        return jsonObject;
    }

    private static JSONObject cronToJsonObject(String cron) {
        JSONObject cronJsonObject = new JSONObject();
        String[] date = cron.split(" ");
        // 处理秒
        cronJsonObject.put("second", strToJsonOnject(date[0]));
        // 处理分
        cronJsonObject.put("minute", strToJsonOnject(date[1]));
        // 处理时
        cronJsonObject.put("hour", strToJsonOnject(date[2]));
        // 处理天
        cronJsonObject.put("day", strToJsonOnject(date[3]));
        // 处理月
        cronJsonObject.put("month", strToJsonOnject(date[4]));
        // 处理周
        JSONObject weekJsonObject = new JSONObject();
        if (date[5].contains("/")) {
            String[] array = date[5].split("/");
            String[] weekArray = array[0].split(",");
            List<String> triggerList = new ArrayList<>();
            for (String week : weekArray) {
                triggerList.add(weekToChinese(week));
            }
            weekJsonObject.put("trigger", triggerList);
            weekJsonObject.put("repeat", array[1]);
        } else {
            String[] weekArray = date[5].split(",");
            List<String> triggerList = new ArrayList<>();
            for (String week : weekArray) {
                triggerList.add(weekToChinese(week));
            }
            weekJsonObject.put("trigger", triggerList);
        }
        cronJsonObject.put("week", weekJsonObject);
        return cronJsonObject;
    }

    /**
     * 详情
     */
    @GetMapping("/detail")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "详情", notes = "传入jobInfo")
    public R<JobInfo> detail(JobInfo jobInfo) {
        JobInfo detail = jobInfoService.getOne(Condition.getQueryWrapper(jobInfo));

        JobInfoVO vo = new JobInfoVO();
        BeanUtils.copyProperties(detail, vo);

        if (StringUtils.isNotBlank(detail.getCron())) {
            vo.setCronStr(detail.getCron());
            JSONObject cronJsonObject = cronToJsonObject(detail.getCron());
            if (null != detail.getStartTime()) {
                cronJsonObject.put("startTime", detail.getStartTime());
            }
            if (null != detail.getEndTime()) {
                cronJsonObject.put("endTime", detail.getEndTime());
            }
            vo.setCron(cronJsonObject.toJSONString());
        }
        return R.data(vo);
    }

    /**
     * 分页
     */
    @GetMapping("/list")
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "分页", notes = "传入jobInfo")
    public R<IPage<JobInfo>> list(JobInfo jobInfo, Query query) {
        IPage<JobInfo> pages = jobInfoService.page(Condition.getPage(query), Condition.getQueryWrapper(jobInfo));
        return R.data(pages);
    }

    /**
     * 自定义分页
     */
    @GetMapping("/page")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "分页", notes = "传入jobInfo")
    public R<IPage<JobInfoVO>> page(JobInfoVO jobInfo, Query query) {
        IPage<JobInfoVO> pages = jobInfoService.selectJobInfoPage(Condition.getPage(query), jobInfo);
        return R.data(pages);
    }

    /**
     * 新增
     */
    @PostMapping("/save")
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "新增", notes = "传入jobInfo")
    @Transactional
    public R<Boolean> save(@Valid @RequestBody JobInfo jobInfo) {

        try {
            boolean flag = false;   //是否新增
            if (null == jobInfo.getId()) {
                String code = serialNumberService.generateSerialNumberByModelCode("corn");
                jobInfo.setId(Long.valueOf(code));
                flag = true;
            }

//		if (StringUtils.isNotBlank(jobInfo.getCron())) {
//
//			// 将前端页面传入的字符串，转换成定时任务表达式
//			JSONObject cornJsonObject = JSONObject.parseObject(jobInfo.getCron());
//			jobInfo.setCron(conversion(cornJsonObject));
//
//			try {
//				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//				if (cornJsonObject.containsKey("startTime") && StringUtils.isNotBlank(cornJsonObject.getString("startTime"))) {
//					String startTime = cornJsonObject.getString("startTime");
//					jobInfo.setStartTime(sdf.parse(startTime));
//				}
//				if (cornJsonObject.containsKey("endTime") && StringUtils.isNotBlank(cornJsonObject.getString("endTime"))) {
//					String endTime = cornJsonObject.getString("endTime");
//					jobInfo.setEndTime(sdf.parse(endTime));
//				}
//			} catch (ParseException pe) {
//				LOGGER.error("日期转换异常", pe);
//			}
//		}
            boolean insert = jobInfoService.saveOrUpdate(jobInfo);
            if (flag) {
                if (insert) {
                    ZjywTxCfg zjywTxCfg = new ZjywTxCfg();
                    zjywTxCfg.setTxCode("group1");
                    List<ZjywTxCfg> zjywTxCfgList = zjywTxCfgService.selectZjywTxCfgList(zjywTxCfg);
                    ZjywTxCfg newZjywTxCfg = zjywTxCfgList.get(0);//取出固定的那一条
                    String code2 = serialNumberService.generateSerialNumberByModelCode("cfg");
                    newZjywTxCfg.setTxCfgKey(code2);
                    newZjywTxCfg.setTxName(jobInfo.getDescription());
                    newZjywTxCfg.setTxCode(jobInfo.getGroupName());
                    return R.status(zjywTxCfgService.insertZjywTxCfg(newZjywTxCfg) > 0);
                } else {
                    return R.fail("插入失败");
                }
            }
            return R.success("修改成功");
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            //手动强制回滚事务，这里一定要第一时间处理
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return R.fail(e.getMessage());
        }
    }

    /**
     * 修改
     */
    @PostMapping("/update")
    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "修改", notes = "传入jobInfo")
    public R<Boolean> update(@Valid @RequestBody JobInfo jobInfo) {
        return R.status(jobInfoService.updateById(jobInfo));
    }


    /**
     * 中文星期转英文星期缩写
     *
     * @param week 中文星期
     * @return 英文星期缩写
     */
    private String weekToEnglish(String week) {
        if("星期一".equals(week)){
            return "MON";
        }
        if("星期二".equals(week)){
            return "TUE";
        }
        if("星期三".equals(week)){
            return "WED";
        }
        if("星期四".equals(week)){
            return "THU";
        }
        if("星期五".equals(week)){
            return "FRI";
        }
        if("星期六".equals(week)){
            return "SAT";
        }
        if("星期日".equals(week)){
            return "SUN";
        }
        return "";
    }

    /**
     * 英文星期转中文
     *
     * @param week 英文星期缩写
     * @return 中文星期
     */
    private static String weekToChinese(String week) {
        if("MON".equals(week)){
            return "星期一";
        }
        if("TUE".equals(week)){
            return "星期二";
        }
        if("WED".equals(week)){
            return "星期三";
        }
        if("THU".equals(week)){
            return "星期四";
        }
        if("FRI".equals(week)){
            return "星期五";
        }
        if("SAT".equals(week)){
            return "星期六";
        }
        if("SUN".equals(week)){
            return "星期日";
        }
        return "";
    }

    /**
     * 新增或修改
     */
    @PostMapping("/submit")
    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "新增或修改", notes = "传入jobInfo")
    public R<Boolean> submit(@Valid @RequestBody JobInfo jobInfo) {

//		if (StringUtils.isNotBlank(jobInfo.getCron())) {
//			// 将前端页面传入的字符串，转换成定时任务表达式
//			JSONObject cornJsonObject = JSONObject.parseObject(jobInfo.getCron());
//			jobInfo.setCron(conversion(cornJsonObject));
//
//			try {
//				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//				if (cornJsonObject.containsKey("startTime") && StringUtils.isNotBlank(cornJsonObject.getString("startTime"))) {
//					String startTime = cornJsonObject.getString("startTime");
//					jobInfo.setStartTime(sdf.parse(startTime));
//				}
//				if (cornJsonObject.containsKey("endTime") && StringUtils.isNotBlank(cornJsonObject.getString("endTime"))) {
//					String endTime = cornJsonObject.getString("endTime");
//					jobInfo.setEndTime(sdf.parse(endTime));
//				}
//			} catch (ParseException pe) {
//				LOGGER.error("日期转换异常", pe);
//			}
//		}

        return R.status(jobInfoService.saveOrUpdate(jobInfo));
    }

    /**
     * 刷新
     */
    @PostMapping("/refresh")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "刷新", notes = "无参")
    public R<String> refresh() {
        try {
            HttpClientResult clientResult = HttpClientUtil.doPostJson(refreshUrl, null, 60000, 60000, 150000);
            if (clientResult.getCode() >= 200 && clientResult.getCode() < 300) {
                return R.data(clientResult.getContent());
            } else {
                return R.fail(clientResult.getContent());
            }
        } catch (IOException e) {
            return R.fail(e.getMessage());
        }
    }


    /**
     * 删除
     */
    @PostMapping("/remove")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "删除", notes = "传入ids")
    @Transactional
    public R<Boolean> remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
        List<String> txCfgKeyList = new ArrayList<>();
        try {
            List<Long> idList = Func.toLongList(ids);
            for (Long id : idList) {
                // 1.先判断 有无关联的开启的批量计划

                JobInfo jobInfo = jobInfoService.getById(id);
                // 1.1 查询关联的 开启的批量记录
                QueryWrapper<BatchRecord> batchRecordQueryWrapper = new QueryWrapper<>();
                batchRecordQueryWrapper.eq("TASK_GROUP", jobInfo.getGroupName());
//				batchRecordQueryWrapper.eq("ZJYW_SYS_DATE", new Date());
                List<BatchRecord> batchRecordOpenList = batchRecordService.list(batchRecordQueryWrapper);
                // 1.2 查询关联的 开启的批量计划
                QueryWrapper<BatchSchedule> batchScheduleQueryWrapper = new QueryWrapper<>();
                batchScheduleQueryWrapper.eq("TASK_GROUP", jobInfo.getGroupName());
                List<BatchSchedule> batchScheduleOpenList = batchScheduleService.list(batchScheduleQueryWrapper);

                if (batchRecordOpenList.isEmpty() && batchScheduleOpenList.isEmpty()) {
                    // 1.2 若无 则可以删除
                    ZjywTxCfg zjywTxCfg = new ZjywTxCfg();
                    zjywTxCfg.setTxCode(jobInfo.getGroupName());
                    List<ZjywTxCfg> zjywTxCfgList = zjywTxCfgService.selectZjywTxCfgList(zjywTxCfg);
                    for (ZjywTxCfg zjywTxCfg1 : zjywTxCfgList) {
                        txCfgKeyList.add(zjywTxCfg1.getTxCfgKey());
                    }
                } else {
                    // 1.1 若有 则不能删除
                    return R.fail("存在关联的 批量任务计划，请先修改关联的任务计划后重试！");
                }
            }

            // 批量删除jobInfo表数据
            jobInfoService.removeByIds(Func.toLongList(ids));

            if (!txCfgKeyList.isEmpty()) {
                // 批量删除 txCfg表数据
                zjywTxCfgService.deleteZjywTxCfgByIds(txCfgKeyList);
            }
            return R.success("删除成功！");
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            //手动强制回滚事务，这里一定要第一时间处理
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return R.fail(e.getMessage());
        }
    }

    /**
     * @param
     * @Description 定时任务查询
     * @Author wjh
     * @Date 2020年12月20日11:41:46
     **/
    @GetMapping("/selectJobinfoName")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "定时任务查询", notes = "无参")
    @ApiLog("定时任务查询")
    @Log(title = "定时任务查询", businessType = BusinessType.SELECT)
    public com.dhcc.bpm.common.json.JSONObject selectBySysName() {
        com.dhcc.bpm.common.json.JSONObject object = new com.dhcc.bpm.common.json.JSONObject();
        object.put("success", true);
        object.put("names", jobInfoService.selectJobinfoName());
        return object;
    }
    /**
        * code review
        * @user daina
        * @date 2021-04-12
     * @desc 生成所属组名称
     */
    /**
     * 生成所属组名称
     */
    @GetMapping("/getGroupName")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "生成所属组名称", notes = "")
    public R getGroupName() {
        List<Integer> list = jobInfoService.getGroupNameNum();
        if (list.size() > 0) {
            int num = list.get(0) + 1;
            return R.data("group" + num);
        }
        return R.data("group0");
    }
}
